import {getRequest, postRequest} from "@/utils/request.js";
import {calculateBigFileMD5, splitFile} from '@/utils/FileUtils.js'
import Pubsub from "pubsub-js";

// 最大分块大小 1MB
const MAX_BLOB_SIZE = 1024*1024
// 上传分块的任务阻塞的最大任务数
const BLOCK_TASK_SIZE = 10

/**
 * 获取上传起始索引
 * @param md5 文件MD5 - 必选
 * @param size 文件大小（单位: B）- 必选
 * @param fileName 文件名（带后缀 - 必选
 * @param chunkSize (非必须)
 * @returns {Promise<unknown>}
 */
export async function getUploadStartIndex(md5, size, fileName, chunkSize = MAX_BLOB_SIZE) {
    try {
        const params = {md5: md5, size: size, fileName: fileName, maxBlobSize: chunkSize};
        return await getRequest('/heaven-server/upload/auth/start', params);
    } catch (e) {
        console.log('获取上传起始索引失败', e);
        return {code: -1, msg: '获取上传起始索引失败', data: {}}
    }
}

export async function uploadBlob(md5, size, fileName, index, blob,chunkSize = MAX_BLOB_SIZE){
    if (! blob instanceof Blob) {
        return {code: -1, msg: 'blob参数错误'}
    }
    try {
        const datas = new FormData()
        datas.set('md5', md5)
        datas.set('size', size)
        datas.set('fileName', fileName)
        datas.set('index', index)
        datas.set('maxBlobSize', chunkSize)
        datas.set('blob', blob)
        datas.set('contentLength', blob.size)
        return await postRequest('/heaven-server/upload/auth/blob/local-cache', datas, {'Content-Type': 'multipart/form-data'});
    } catch (e) {
        console.log('上传文件失败', e);
        return {code: -1, msg: '上传文件失败'}
    }
}

/**
 * 文件上传, 失败再次调用即可
 * 用 Pubsub-js 发布`upload-blob`事件，模拟上传进度, 格式：{current, total}
 * @param file
 * @returns {code, msg, data}
 *  code: 0 表示成功，其他表示失败
 *  data: 在code为0时，必定包含两个参数 bucketName, ObjectName
 */
export async function uploadFile(file) {
    if(! file instanceof File) {
        return {code: -1, msg: 'file参数错误，必须是 File 类型'}
    }
    const fileName = file.name
    const size = file.size
    const md5 = await calculateBigFileMD5(file, MAX_BLOB_SIZE);

    // 文件分块列表
    const blobList = splitFile(file, MAX_BLOB_SIZE)

    // 获取上传起始索引
    const res1 = await getUploadStartIndex(md5, size, fileName)
    res1.data['md5'] = md5
    if(res1.code === 0 && !res1.data.bigFileExist && res1.data.index < blobList.length) {
        const startIndex = res1.data.index
        const total = blobList.length
        let tasks = []
        let resData = {}
        // 开始上传分块
        console.log(`begin to upload blob from ${startIndex}`)
        let success = false
        let errMsg = ''
        // 前n-1个任务并发执行，最后一个任务等待前n-1个任务全部完成后再执行
        for (let i = startIndex; i < total-1; i++) {
            const task = uploadBlob(md5, size, fileName, i, blobList[i])
                .then(res2 => {
                    if(res2.code === 0 ) {
                        Pubsub.publish('upload-blob', {
                            current: i,
                            total: total
                        })
                        if (res2.data.status === 1) {
                            success = true
                        }
                    } else {
                        errMsg = res2.msg
                    }
                    resData = {...res2.data}
                })
            tasks.push(task)
            if (tasks.length >= 20) {
                await Promise.all(tasks)
                tasks = []
            }
        }
        if (tasks.length > 0) {
            await Promise.all(tasks)
        }
        const lastIndex = total - 1
        await uploadBlob(md5, size, fileName, lastIndex, blobList[lastIndex])
            .then(res2 => {
                if(res2.code === 0 ) {
                    Pubsub.publish('upload-blob', {
                        current: total,
                        total: total
                    })
                    if (res2.data.status === 1) {
                        success = true
                    }
                } else {
                    errMsg = res2.msg
                }
                resData = {...res2.data}
            })

        resData['md5'] = md5
        if (success) {
            return {code: 0, msg: `${fileName} 上传成功`, data: resData}
        } else {
            return {code: -1, msg: `${fileName} 上传失败,请从新上传，${errMsg}`, data: resData}
        }
    } else if(res1.code !== 0) {
        return {code: -1, msg: '获取上传起始索引失败'+res1.msg, data: res1.data}
    } else if (res1.data.bigFileExist) {
        return {code: 1, msg: `文件已存在，无需上传-${fileName}`, data: res1.data}
    }
}

export async function uploadSingleWhole(md5,fileName,size,file) {
    const datas = new FormData()
    datas.set('md5', md5)
    datas.set('size', size)
    datas.set('fileName', fileName)
    datas.set('file', file)
    try {
        return  await postRequest('/heaven-server/upload/auth/single-whole', datas, {'Content-Type': 'multipart/form-data'});
    } catch (e) {
        console.log('上传图片失败',e)
        return {code: -1, msg: '上传图片失败'}
    }
}